﻿/*  ----------------------------------------------------------------------------
 *  Warren Moxley
 *  ----------------------------------------------------------------------------
 *  GTasksSync
 *  ----------------------------------------------------------------------------
 *  File:       ThisAddIn.cs
 *  Author:     IT1SERVICES\wmoxley
 *  ----------------------------------------------------------------------------
	Licensed to the Apache Software Foundation (ASF) under one
       	or more contributor license agreements.  See the NOTICE file
       	distributed with this work for additional information
       	regarding copyright ownership.  The ASF licenses this file
       	to you under the Apache License, Version 2.0 (the
       	"License"); you may not use this file except in compliance
       	with the License.  You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

       	Unless required by applicable law or agreed to in writing,
       	software distributed under the License is distributed on an
       	"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
       	KIND, either express or implied.  See the License for the
       	specific language governing permissions and limitations
       	under the License. 
 *  ----------------------------------------------------------------------------
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
using GoogleHelper;
using System.Windows.Forms;
using Utility.ModifyRegistry;
using Microsoft.Win32;
using Google.Apis.Wrappers.Tasks;
using Google.Apis.Wrappers.Tasks.Data;
using System.Globalization;
using System.ComponentModel;

namespace GTasksSync
{
    public partial class ThisAddIn
    {
        private const string SeedKey = "SDVDSfjdsklfjasdf8u3w34rwerrjkldvavjlkasdfjwe45324r3240923r923";
        private Office.CommandBar _objMenuBar;
        private Office.CommandBarPopup _objNewMenuBar;

        private Office.CommandBarButton _objSyncButton;
        private Office.CommandBarButton _objConfigButton;


        private string menuTag = "GTasksSync";
        private string menuLabel = "GTasks Sync";
        
        private string _id;
        public string Id
        {
            get { return _id; }
            set { _id = value; }
        }

        private string userName;
        private string password;
        private int refreshMinutes;
        private DateTime lastRefresh;
        private Timer timer;
        private bool bSyncRunning = false;

        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {
            if (loadConfig())
                loadUI();

        }

        private void loadUI()
        {
            this.MyMenuBar();

            if (refreshMinutes > 0)
            {
                timer = new Timer()
                {
                    Interval = 60000,
                    Enabled = true
                };

                timer.Tick += delegate
                {
                    if (lastRefresh.AddMinutes(refreshMinutes) <= DateTime.Now)
                        doSync();

                };

            }

        }

        private bool loadConfig()
        {
            ModifyRegistry reg = new ModifyRegistry();

            reg.BaseRegistryKey = Registry.CurrentUser;

            try
            {
                string strProfileName = Application.Session.CurrentProfileName;

                if (string.IsNullOrEmpty(strProfileName))
                    strProfileName = "Default";

                userName = Util.Encryption.DecryptString(reg.Read(strProfileName + "-gUser"), SeedKey);
                password = Util.Encryption.DecryptString(reg.Read(strProfileName + "-gPass"), SeedKey);

                if (
                    !int.TryParse(reg.Read(strProfileName + "-gRefreshMin"), out refreshMinutes) ||
                    (
                        refreshMinutes > 0 &&
                        (
                            string.IsNullOrEmpty(userName) ||
                            string.IsNullOrEmpty(password)
                        )
                    )
                )
                {
                    MessageBox.Show("Configuration Missing.  Starting Configuration Now...");
                    showConfig();
                    return false;

                }

            }
            catch (Exception ex)
            {
                MessageBox.Show("Configuration Missing.  Starting Configuration Now...");
                showConfig();
                return false;

            }

            return true;

        }

        private void showConfig()
        {
            frmSettings frmSettings = new GTasksSync.frmSettings(this.Application, SeedKey);

            frmSettings.FormClosed += delegate
            {
                frmSettings.Dispose();
                if (loadConfig())
                    loadUI();

            };

            frmSettings.ShowDialog();

        }

        private List<Outlook.TaskItem> getLocalTasks()
        {
            List<Outlook.TaskItem> itemRet = new List<Outlook.TaskItem>();

            Outlook.Folder folder = Application.Session.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderTasks) as Outlook.Folder;

            foreach (Outlook.TaskItem tItem in folder.Items)
            {
                itemRet.Add(tItem);

            }

            return itemRet;

        }

        private TaskList getRemoteTaskList(string taskListTitle)
        {
            TaskListHelper taskLists = new TaskListHelper(userName, password);

            string pageToken = "";

            TaskList list = null;

            do
            {
                var taskListRet = taskLists.GetTaskLists(pageToken);
                pageToken = taskListRet.NextPageToken;

                foreach (var taskList in taskListRet.Items)
                {
                    if (taskList.Title.ToLower() == taskListTitle.ToLower())
                    {
                        list = taskList;
                        break;
                    }

                }

                if (list != null)
                    break;

            }
            while (!string.IsNullOrEmpty(pageToken));

            list = taskLists.GetTaskList(list.Id);

            return list;

        }

        private List<Task> getRemoteTasks(string taskListTitle)
        {
            List<Task> taskRet = new List<Task>();

            lastRefresh = DateTime.Now;

            string pageToken = "";

            var list = getRemoteTaskList(taskListTitle);

            if (list == null || list.Etag == null)
                return taskRet;

            TaskHelper taskHelper = new TaskHelper(userName, password);

            pageToken = "";

            do
            {
                var tasks = taskHelper.GetTasks(list.Id, pageToken, true, true, true);
                pageToken = tasks.NextPageToken;

                foreach (var task in tasks.Items)
                {
                    var locTask = taskHelper.GetTask(task.Id, list.Id);
                    taskRet.Add(locTask);
                }

            }

            while (!string.IsNullOrEmpty(pageToken));

            return taskRet;

        }

        private void doSync()
        {
            if (bSyncRunning)
                return;

            bSyncRunning = true;

            lastRefresh = DateTime.Now;

            try
            {
                string TaskListTitle = "Default List";
                TaskList taskList = getRemoteTaskList(TaskListTitle);

                List<Task> gTasks = getRemoteTasks(taskList.Title);
                List<Outlook.TaskItem> localTasks = getLocalTasks();

                foreach (var gTask in gTasks)
                {
                    Outlook.TaskItem lTask = null;
                    foreach (var localTask in localTasks)
                    {
                        if (gTask.Title == localTask.Subject)
                        {
                            lTask = localTask;
                            break;

                        }

                    }

                    if (!string.IsNullOrEmpty(gTask.Title))
                        if (!gTask.Deleted && lTask == null)
                            CreateLocalTask(gTask, taskList.Title);
                        else if (lTask != null)
                            if (gTask.Deleted)
                                lTask.Delete();
                            else
                                UpdateTask(gTask, lTask, taskList.Title);

                    localTasks = getLocalTasks();

                }

                var gTaskList = getRemoteTaskList(taskList.Title);

                if (gTaskList != null)
                    new TaskListHelper(userName, password).ClearCompletedTaskList(gTaskList.Id);

                localTasks = getLocalTasks();
                gTasks = getRemoteTasks(TaskListTitle);

                foreach (var lTask in localTasks)
                {
                    Task rTask = null;

                    foreach (var gTask in gTasks)
                    {
                        if (gTask.Title == lTask.Subject)
                        {
                            rTask = gTask;
                            break;

                        }

                    }

                    if (rTask == null)
                        CreateRemoteTask(lTask, TaskListTitle);
                    else
                        UpdateTask(rTask, lTask, TaskListTitle);

                }

            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "Error Occurred");

            }
        
            bSyncRunning = false;

        }

        private DateTime convertToLocalTime(string time)
        {
            try
            {
                return DateTime.Parse(time);

            }
            catch (Exception ex)
            {
                return DateTime.MinValue;

            }

        }

        private DateTime convertToLocalDate(string time)
        {
            try
            {
                DateTime dtParse = DateTime.Parse(time).ToUniversalTime();

                return DateTime.SpecifyKind(new DateTime(dtParse.Year, dtParse.Month, dtParse.Day, 0, 0, 0), DateTimeKind.Local);

            }
            catch (Exception ex)
            {
                return DateTime.MinValue;

            }

        }

        private string convertFromLocalTime(DateTime dtVal)
        {
            if (DateTime.Now.Year + 10 < dtVal.Year || DateTime.Now.Year - 10 > dtVal.Year)
                return "";

            return dtVal.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK");
        }

        private string convertFromLocalDate(DateTime dtVal)
        {
            if (DateTime.Now.Year + 10 < dtVal.Year || DateTime.Now.Year - 10 > dtVal.Year)
                return "";

            return DateTime.SpecifyKind(new DateTime(dtVal.Year, dtVal.Month, dtVal.Day, 0,0,0),DateTimeKind.Utc).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK");
        }

        private void CreateLocalTask(Task task, string taskListTitle)
        {
            if (task.Deleted)
                return;

            Outlook.TaskItem lTask = (Outlook.TaskItem)Application.CreateItem(Outlook.OlItemType.olTaskItem);

            lTask.Subject = task.Title;
            lTask.Body = task.Notes;
            
            lTask.Complete = (task.Hidden || task.Status == "needsAction" ? false : true);

            if (!string.IsNullOrEmpty(task.Due))
                lTask.DueDate = convertToLocalDate(task.Due);

            if (lTask.Complete)
                if (!string.IsNullOrEmpty(task.Completed))
                    lTask.DateCompleted = convertToLocalTime(task.Completed);

            lTask.StartDate = DateTime.Now;

            lTask.Save();

        }

        /*
        private TaskList createTaskList(string taskListTitle)
        {
            try
            {
                TaskList tl = new TaskList()
                {
                    Kind = "tasks#taskList",
                    Title = taskListTitle
                };

                TaskListHelper tlh = new TaskListHelper(userName, password);

                tl = tlh.InsertTaskList(tl);

                return tl;

            }
            catch (Exception ex)
            {
                return null;

            }

        }
        */

        private void CreateRemoteTask(Outlook.TaskItem task, string taskListTitle)
        {
            TaskHelper th = new TaskHelper(userName, password);

            Task gTask = new Task();

            var taskList = getRemoteTaskList(taskListTitle);

            if (string.IsNullOrEmpty(task.Subject))
                return;

            gTask.Title = task.Subject;
            gTask.Notes = task.Body;

            if (task.Complete)
                gTask.Status = "completed";
            else
                gTask.Status = "needsAction";

            if (task.DateCompleted != null)
                gTask.Completed = convertFromLocalTime(task.DateCompleted);

            if(string.IsNullOrEmpty(gTask.Completed))
                gTask.Completed = null;

            gTask.Deleted = false;

            if (task.DueDate != null)
                gTask.Due = convertFromLocalDate(task.DueDate);

            if(string.IsNullOrEmpty(gTask.Due))
                gTask.Due = null;

            gTask.Updated = convertFromLocalTime(DateTime.Now);

            //This is a fix to remove any fields that aren't to be sent to google. IE only the updateable fields.
            //gTask.Deleted = null;
            //gTask.Hidden = null;
            gTask.SelfLink = null;
            gTask.Parent = null;
            gTask.Position = null;
            gTask.Updated = null;
            //remoteTask.Due = null;
            //remoteTask.Completed = null;

            try
            {
                if (th.InsertTask(gTask, taskList.Id, null, null).Id == null)
                    MessageBox.Show("Error Saving Remote Task: " + gTask.Title);

            }
            catch (Exception ex)
            {
                MessageBox.Show("Error Saving Remote Task: " + gTask.Title + Environment.NewLine + "Error: " + ex.Message);

            }
        }

        private void UpdateTask(Task remoteTask, Outlook.TaskItem localTask, string taskListTitle)
        {
            if (convertToLocalTime(remoteTask.Updated) == localTask.LastModificationTime)
                return;
            else if (convertToLocalTime(remoteTask.Updated) > localTask.LastModificationTime)
            {
                if (remoteTask.Deleted)
                    return;

                localTask.Complete = (remoteTask.Hidden || remoteTask.Status == "needsAction" ? false : true);

                if (!string.IsNullOrEmpty(remoteTask.Due))
                {
                    DateTime dtNewDue = convertToLocalDate(remoteTask.Due);

                    if(dtNewDue < localTask.StartDate)
                        localTask.StartDate = dtNewDue;

                    localTask.DueDate = dtNewDue;
                }
                else
                    localTask.DueDate = new DateTime(4501, 1, 1);

                if (localTask.Complete)
                    if (!string.IsNullOrEmpty(remoteTask.Completed))
                        localTask.DateCompleted = convertToLocalTime(remoteTask.Completed);
                    else
                        localTask.DateCompleted = new DateTime(4501, 1, 1);

                localTask.Body = remoteTask.Notes;
                localTask.StartDate = localTask.DueDate;

                localTask.Save();

            }
            else
            {
                TaskHelper th = new TaskHelper(userName, password);
                
                var taskList = getRemoteTaskList(taskListTitle);

                remoteTask.Title = localTask.Subject;
                remoteTask.Notes = localTask.Body ?? "";

                if (localTask.Complete)
                    remoteTask.Status = "completed";
                else
                    remoteTask.Status = "needsAction";

                if (localTask.DateCompleted != null)
                    remoteTask.Completed = convertFromLocalTime(localTask.DateCompleted).ToString();
                else
                    remoteTask.Completed = null;

                if (string.IsNullOrEmpty(remoteTask.Completed))
                    remoteTask.Completed = null;

                remoteTask.Deleted = false;

                if (localTask.DueDate != null)
                    remoteTask.Due = convertFromLocalDate(localTask.DueDate).ToString();
                else
                    remoteTask.Due = null;

                if (string.IsNullOrEmpty(remoteTask.Due))
                    remoteTask.Due = null;

                remoteTask.Updated = convertFromLocalTime(DateTime.Now).ToString();

                if (string.IsNullOrEmpty(remoteTask.Updated))
                    remoteTask.Updated = null;

                //This is a fix to remove any fields that aren't to be sent to google. IE only the updateable fields.
                //remoteTask.Deleted = null;
                //remoteTask.Hidden = null;
                remoteTask.SelfLink = null;
                remoteTask.Parent = null;
                remoteTask.Position = null;
                remoteTask.Updated = null;
                //remoteTask.Due = null;
                //remoteTask.Completed = null;

                try
                {
                    if (th.UpdateTask(remoteTask, remoteTask.Id, taskList.Id).Id == null)
                        MessageBox.Show("Error Saving Remote Task: " + remoteTask.Title);

                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error Saving Remote Task: " +  remoteTask.Title + Environment.NewLine + "Error: " + ex.Message);
                
                }

            }

        }


        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
        {
            timer.Stop();
            timer.Dispose();

        }

        #region VSTO generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InternalStartup()
        {
            this.Startup += new System.EventHandler(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }

        #endregion

        #region "Event Handler"

        #region "Menu Button"

        void _objSyncButton_Click(Office.CommandBarButton Ctrl, ref bool CancelDefault)
        {
                BackgroundWorker bgw = new BackgroundWorker();

                bgw.DoWork += delegate
                {
                    try
                    {
                        doSync();
                    }
                    catch (System.Exception ex)
                    {
                        System.Windows.Forms.MessageBox.Show("Error " + ex.Message.ToString());
                    }

                };

                bgw.RunWorkerAsync();

            
        }

        void _objConfigButton_Click(Office.CommandBarButton Ctrl, ref bool CancelDefault)
        {
            try
            {
                showConfig();
            }
            catch (System.Exception ex)
            {
                System.Windows.Forms.MessageBox.Show("Error " + ex.Message.ToString());
            }

        }


        #endregion

        #region "Remove Existing"

        private void ErsMyMenuBar()
        {
            // If the menu already exists, remove it.
            try
            {
                Office.CommandBarPopup _objIsMenueExist = (Office.CommandBarPopup)
                    this.Application.ActiveExplorer().CommandBars.ActiveMenuBar.
                    FindControl(Office.MsoControlType.msoControlPopup
                              , missing
                              , menuTag
                              , true
                              , true);

                if (_objIsMenueExist != null)
                {
                    _objIsMenueExist.Delete(true);
                }
            }
            catch (System.Exception ex)
            {
                System.Windows.Forms.MessageBox.Show("Error: " + ex.Message.ToString()
                                                   , "Error Message");
            }
        }

        #endregion

        #endregion

        #region "Outlook07 Menu"

        private void MyMenuBar()
        {
            this.ErsMyMenuBar();
            try
            {
                //Define the existent Menu Bar
                _objMenuBar = this.Application.ActiveExplorer().CommandBars.ActiveMenuBar;
                //Define the new Menu Bar into the old menu bar
                _objNewMenuBar = (Office.CommandBarPopup)
                                 _objMenuBar.Controls.Add
                (Office.MsoControlType.msoControlPopup
                                                        , missing
                                                        , missing
                                                        , missing, false);
                if (_objNewMenuBar != null)
                {
                    _objNewMenuBar.Caption = menuLabel;
                    _objNewMenuBar.Tag = menuTag;
                    _objSyncButton = (Office.CommandBarButton)_objNewMenuBar.Controls.
                    Add(Office.MsoControlType.msoControlButton, missing,
                        missing, 1, true);
                    _objSyncButton.Style = Office.MsoButtonStyle.
                        msoButtonIconAndCaption;
                    _objSyncButton.Caption = "Sync Now";
                    //Icon 
                    _objSyncButton.FaceId = 500;
                    _objSyncButton.Tag = "SyncNow";

                    if (refreshMinutes == 0)
                        _objSyncButton.Enabled = false;
                    else
                        _objSyncButton.Enabled = true;

                    //EventHandler
                    _objSyncButton.Click += new Office._CommandBarButtonEvents_ClickEventHandler(_objSyncButton_Click);
                    _objConfigButton = (Office.CommandBarButton)_objNewMenuBar.Controls.
                   Add(Office.MsoControlType.msoControlButton, missing,
                       missing, 1, true);
                    _objConfigButton.Style = Office.MsoButtonStyle.
                        msoButtonIconAndCaption;
                    _objConfigButton.Caption = "Settings...";
                    //Icon 
                    _objConfigButton.FaceId = 500;
                    _objConfigButton.Tag = "Settings";
                    //EventHandler
                    _objConfigButton.Click += new Office._CommandBarButtonEvents_ClickEventHandler(_objConfigButton_Click);
                    _objNewMenuBar.Visible = true;
                }
            }
            catch (System.Exception ex)
            {
                System.Windows.Forms.MessageBox.Show("Error: " + ex.Message.ToString()
                                                   , "Error Message");
            }
        }

        #endregion
    
    }

}
